import { MaybeComputedElementRef, unrefElement } from '@vueuse/core'
import { defineComponent, ref, createApp, TransitionGroup, ExtractPropTypes } from 'vue'
import Message, { MessageProps } from './Message'

let seed = 0

function getUUID() {
  return '___' + seed++
}

export type MessageManagerExpose = {
  add: (options: MessageProps) => MessageProps & { close: () => void }
  remove: (key: string | number) => void
  removeAll: () => void
}

export const messageManagerProps = {
  prefixCls: String,
  max: Number
}

type MessageManagerProps = ExtractPropTypes<typeof messageManagerProps>

const MessageManager = defineComponent({
  name: 'message-manager',
  props: messageManagerProps,
  setup(props, { expose }) {
    const msgListRef = ref<MessageProps[]>([])

    function add(options: MessageProps): MessageProps {
      const key = options?.key ?? getUUID()
      const msg = {
        ...options,
        key,
        close: () => remove(key)
      }
      const i = msgListRef.value.findIndex(msg => msg.key === key)
      if (~i) {
        msgListRef.value.splice(i, 1, msg)
      } else {
        if (msgListRef.value.length >= props.max!) {
          msgListRef.value.shift()
        }
        msgListRef.value.push(msg)
      }
      return msg
    }
    function remove(key: string | number) {
      const i = msgListRef.value.findIndex(msg => msg.key === key)
      if (~i) msgListRef.value.splice(i, 1)
    }
    function removeAll() {
      msgListRef.value = []
    }

    expose({
      add,
      remove,
      removeAll
    })

    return () => {
      const { prefixCls } = props
      const cls = `${prefixCls}-container`

      return (
        <TransitionGroup tag='div' name={prefixCls} class={cls}>
          {msgListRef.value.map(msg => (
            <Message {...msg} onClose={() => remove(msg.key!)} />
          ))}
        </TransitionGroup>
      )
    }
  }
})

export function newInstace(props: MessageManagerProps & { to?: MaybeComputedElementRef }) {
  const div = document.createElement('div')
  const container = unrefElement(props.to) || document.body
  container.appendChild(div)

  const instRef = ref<MessageManagerExpose>()
  const app = createApp({
    render: () => <MessageManager {...props} ref={instRef} />
  })
  app.mount(div)
  return instRef.value!
}

export default MessageManager
